#include <control_terminal.h>

#include <utils/pthread_mutex_locker.h>

#include <stdexcept>
#include <cassert>
//------------------------------------------------------------------------------
#include <control_signal_dispatcher.h>
#include <control_command_dispatcher.h>
#include <control_event_dispatcher.h>
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
ControlTerminal::ControlTerminal(char const * conf)
    : config_(conf)
    , modemCtrl_(*this) // check config validity
    , videoCtrl_(*this) // check config validity
    , eventDisp_()
{ }
//------------------------------------------------------------------------------
inline bool ControlTerminal::emit(ControlEvent const & event)
{
    bool ok;
    {
        utils::pthread_mutex_locker lock(queueMutex_);
        ok = queue_.enqueue(event);
    }
    if(ok)
    {
        assert(eventDisp_ != 0);

        eventDisp_->notify(); //This is safe
    }
    return ok;
}
//------------------------------------------------------------------------------
bool ControlTerminal::warning()
{
    struct Warning : ControlEvent
    {
        Warning() : ControlEvent(0, &ControlTerminal::onWarning)
        { }
    };
    return emit(Warning());
}

bool ControlTerminal::alarm()
{
    struct Alarm : ControlEvent
    {
        Alarm() : ControlEvent(0, &ControlTerminal::onAlarm)
        { }
    };
    return emit(Alarm());
}
//------------------------------------------------------------------------------
bool ControlTerminal::on_data_in(ControlDispatcher * ctx)
{
    return true;
}
bool ControlTerminal::on_data_out(ControlDispatcher * ctx)
{
    return true;
}
bool ControlTerminal::on_hangup(ControlDispatcher * ctx)
{
    return true;
}
bool ControlTerminal::on_error(ControlDispatcher * ctx, int err)
{
    return true;
}
//------------------------------------------------------------------------------
bool ControlTerminal::onWarning(BaseEvent const & event)
{
    printf("%s\n", __PRETTY_FUNCTION__);
    return true;
}

bool ControlTerminal::onAlarm(BaseEvent const & event)
{
    printf("%s\n", __PRETTY_FUNCTION__);
    return true;
}
//------------------------------------------------------------------------------
int ControlTerminal::exec()
{
    utils::signal_blocker sigBlock;

    SignalDispatcher  sig(sigBlock);
    add_event<NOTIFY_IN>(sig.fd(), &sig);

    CommandDispatcher con;
    add_event<NOTIFY_IN>(con.fd(), &con);

    EventDispatcher   edp(eventDisp_);
    add_event<NOTIFY_IN>(edp.fd(), &edp);

    ModemControlService::Manager modem(modemCtrl_); // start thread
    //VideoControlService::Manager video(videoCtrl_); // start thread

    notify_status status;
    do
    {
        switch(status = wait())
        {
        case NOTIFY_SUCCESS:
            ;
            break;
        default:
            ; // suppress the warning
        }
    }
    while(status != NOTIFY_INTERRUPT);

    return 0;
}
//------------------------------------------------------------------------------
bool ControlTerminal::onSignal(int signo)
{
    switch(signo)
    {
    case SIGQUIT:
        printf("SIGQUIT\n");
        return false;
    case SIGINT:
        printf("SIGINT\n");
        return false;
    case SIGTERM:
        printf("SIGTERM\n");
        return false;
    case SIGTSTP:
        printf("SIGTSTP\n");
        return false;
    default:
        ;
    }
    return true;
}

bool ControlTerminal::onCommand(char const * comm, size_t len)
{
    struct Command
    {
        char const * comm;
        size_t       len;

        bool (ControlTerminal::* func)();

    } const commands[] =
    {
        { "exit",    4, 0 }
      , { "warning", 7, &ControlTerminal::warning }
      , { "alarm",   5, &ControlTerminal::alarm   }
    };

    for(size_t i = 0; i < sizeof(commands)/sizeof(*commands); ++i)
    {
        Command const * curr = commands + i;

        if(std::strncmp(comm, curr->comm, curr->len) == 0)
        {
            bool ret;
            printf("OK+ [%s]\n", curr->comm);
            if((ret = curr->func))
            {
                (this->*(curr->func))();
            }
            return ret;
        }
    }
    printf("ERR- unknown command [%.*s]\n", len, comm);
    return true;
}
//------------------------------------------------------------------------------
bool ControlTerminal::onEvent(uint64_t counter)
{
    bool cont = true;
    while(counter-- > 0)
    {
        ControlEvent event;

        queueMutex_.lock();
        bool const ok = queue_.dequeue(event);
        queueMutex_.unlock();

        if(!ok)
        {
            throw std::logic_error("queue is empty");
        }
        if(!event.action(this))
        {
            cont = false;
        }
    }
    return cont;
}
//------------------------------------------------------------------------------
